/*
 * ﻿Copyright (C) 2009-2013 NewMain Softech
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package com.newmainsoftech.gwetons.client.style.css;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;

import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap;
import com.newmainsoftech.gwetons.client.logging.JsniLoggerWrapFactory;
import com.newmainsoftech.gwetons.client.logging.JsniLoggerWrapFactoryImpl;
import com.newmainsoftech.gwetons.shared.logging.LoggerWrap;

/**
 * With <code>CssStyleChangerBySelector</code>, CSS style can have progressive changed dynamically. <br />
 * The style change data will be read as JSON from hosting web page. <br />
 * The format of the style change data input:
 * <blockquote>
 * <code>
 * cscbsDataSet: {<br />
 * &nbsp;&nbsp;styleChangerDataArray: [<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;{<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;repeatable:false, sequence:false<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selectorUnitArray: [<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector:&quot;className&quot;,<br /> 
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;style:&quot;background-color&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isNumericColor:true,<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initValue:&quot;0&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;endValue:&quot;0xffffff&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valueUnit:&quot;%&quot;,<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valuePrefix:null, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valueSuffix:null, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;period:1000, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interval:100, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reversible:false <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector:&quot;className&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;style:&quot;background-color&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isNumericColor:true, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initValue:&quot;0&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;endValue:0xffffff, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valueUnit:&quot;%&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valuePrefix:null, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valueSuffix:null, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;period:2500, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interval:250, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reversible:false<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;] // selectorUnitArray<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;},<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;{<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;repeatable:true, sequence:true <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selectorUnitArray: [<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector:&quot;className&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;style:&quot;background-color&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isNumericColor:true, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initValue:&quot;0&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;endValue:&quot;0xffffff&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valueUnit:&quot;%&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valuePrefix:null, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valueSuffix:null, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;period:5000, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interval:500 <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector:&quot;className&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;style:&quot;background-color&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isNumericColor:true, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initValue:&quot;0&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;endValue:0xffffff, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valueUnit:&quot;%&quot;, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valuePrefix:null, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valueSuffix:null, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;period:5000, <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interval:500 <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;] // selectorUnitArray <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;} <br />
 * &nbsp;&nbsp;] // styleChangerDataArray <br />
 * } // cscbsDataSet <br />
 * </code>
 * <br />
 * </blockquote>
 * <ul>
 * <li><code>cscbsDataSet</code> data unit is represented by {@link CssStyleChangerDataSet} class.
 * </li>
 * <li>Each element of <code>styleChangerDataArray</code> javascript array is represented by 
 * {@link CssStyleChangerDataUnit} class.
 * <ul>
 * <li><code>repeatable</code> : controls whether starting over when finished processing 
 * the all elements of a <code>selectorUnitArray</code> javascript array. 
 * </li>		
 * <li><code>sequence</code> : controls how to process elements of a <code>selectorUnitArray</code> 
 * javascript array. <br />
 * When true, elements in a <code>selectorUnitArray</code> will be processed one by one. When false, 
 * all elements will be processed at once.
 * </li>
 * </ul> 
 * </li>
 * <li>Each element of <code>selectorUnitArray</code> javascript array is represented by 
 * {@link DataUnitByStyleSelector} class.
 * <ul>
 * <li><code>selector</code> : the name of CSS style class.
 * </li>
 * <li><code>style</code> : the name of CSS style.
 * </li>
 * <li><code>isNumericColor</code> : indicates whether the values provided by the <code>initValue</code> 
 * property and the <code>endValue</code> property are numeric values. 
 * </li>
 * <li><code>initValue</code> : the initial value of specified style.
 * <ul>
 * <li>Can be number string, text string or null. Only either decimal or hex formats are supported as  
 * numeric string. String neither decimal nor hex format will be considered as text string.
 * </li>
 * <li>When this is null, then the <code>endValue</code> property value will be set after elapsing 
 * time what the <code>period</code> property specify. <br />
 * The values of the <code>interval</code> property will be ignored. The value of 
 * the <code>reversal</code> property must be false.
 * </li>
 * <li>When this is empty string, then specified style will be cleared. <br />
 * The values of the <code>interval</code> property will be ignored. The value of 
 * the <code>reversal</code> property must be false.
 * </li>
 * <li>If inconsistency with the <code>endValue</code> property value exists (i.e. 
 * the <code>initValue</code> property value is a numeric string and the <code>endValue</code>'s one is 
 * not a numeric string), then the <code>interval</code> property value will be ignored, and the value of 
 * this property will be treated as text string even it's string representation of numeric value.
 * </li>
 * <li>When the <code>valueUnit</code> property is provided, this value should be a numeric string.<br />
 * If this value is text string, then the <code>valueUnit</code> property will be ignored.
 * </li>
 * <li>When this value is text string, the <code>interval</code> property is ignored.
 * </li>
 * </ul>
 * </li>
 * <li><code>endValue</code> : the end value of specified style.
 * <ul>
 * <li>Can be number string or text string. Only either decimal or hex formats are supported as  
 * numeric string. String neither decimal nor hex format will be considered as text string.
 * </li>
 * <li>When this value is null, then the style will be left with the <code>intValue</code> property value 
 * having set before. <br />
 * The <code>interval</code> property will be ignored. The <code>reversal</code> property value must 
 * be false.
 * </li>
 * <li>When this value is empty string, then specified style will be cleared at the end of period. <br />
 * The <code>interval</code> property will be ignored. The <code>reversal</code> property value must 
 * be false.
 * </li>
 * <li>If inconsistency with the <code>initValue</code> property value exists (i.e. 
 * the <code>initValue</code> property value is a numeric string and the <code>endValue</code>'s one is 
 * not a numeric string), then the <code>interval</code> property value will be ignored, and the value of 
 * this property will be treated as text string even it's string representation of numeric value.
 * </li>
 * <li>When the <code>valueUnit</code> property is provided, this value should be a numeric string.<br />
 * If this value and the <code>initValue</code> property value are text string, then 
 * the <code>valueUnit</code> property will be ignored.<br />
 * When this value is neither numeric string nor numeric value and the <code>initValue</code> property 
 * is not provided, then the <code>valueUnit</code> property will be ignored.
 * </li>
 * <li>When this value is text string, the <code>interval</code> property is ignored.
 * </li>
 * </ul>
 * </li>
 * <li><code>valueUnit</code> : string such as &quot; px&quot;, &quot;em &quot;, &quot;%&quot;, etc. <br />
 * Will be ignored for color.
 * </li>
 * <li><code>valuePrefix</code> : string used for the case like opacity in IE 
 * (i.e. &quot;alpha(opacity=&quot; or &quot;'progid:DXImageTransform.Microsoft.Alpha(Opacity=&quot;).
 * </li>
 * <li><code>valueSuffix</code> : string used for the case like opacity in IE (i.e. &quot;)&quot;).
 * </li>
 * <li><code>period</code> : int value as the time period in milli-second.<br />
 * When this value is not bigger than or equal to 0, then the <code>initValue</code> property will be 
 * ignored, and the <code>endValue</code> property value will take effect immediately.
 * </li>
 * <li><code>interval</code> : int value as the interval length in milli-second.<br />
 * <ul>
 * <li>When this value is not numeric, then the whole data set containing that selectorUnitArray property 
 * will be ignored.
 * </li>
 * <li>When this value is not bigger than the minimum interval value being obtained by 
 * {@link #getMinimumInterval()} method, then the value returned by <code>getMinimumInterval()</code> 
 * method will be used instead. 
 * The default minimum interval value is {@value #Default_Minimum_Interval}[ms].
 * </li>
 * <li>When this value is bigger than the <code>period</code> property value, then this value will 
 * be ignored.
 * </li>
 * </ul>
 * </li>
 * <li><code>reversible</code> : controls whether reversing the style change.
 * <ul>
 * <li>When this value is true, then the total period will become 2 times of the period value specified 
 * with the <code>period</code> property, because of reversal.
 * </li>
 * <li>When the type of this value is not boolean, then this value is considered as false.
 * </li>
 * </ul>
 * </li>
 * </ul>
 * </li>
 * </ul> 
 * 
 * 
 * @author Arata Y.
 */
public class CssStyleChangerBySelector {
	// For logging --------------------------------------------------------------------------------
	static String getNameForLogging() {
		return CssStyleChangerBySelector.class.getName();
	}
	
	/* Synchronization over jsniLogUtilFactoryInterface member field is not necessary since 
	 * JavaScript is single thread environment.
	 */
	private static JsniLoggerWrapFactory jsniLogUtilFactoryInterface;
		protected static JsniLoggerWrapFactory getJsniLogUtilFactoryInterface() {
			return jsniLogUtilFactoryInterface;
		}
		protected static void setJsniLogUtilFactoryInterface(
				JsniLoggerWrapFactory jsniLogUtilFactoryInterface) {
			CssStyleChangerBySelector.jsniLogUtilFactoryInterface = jsniLogUtilFactoryInterface;
			setLogUtil( null);
			setJsniLogUtil( null);
		}

	/* Synchronization over logUtil member field is not necessary since 
	 * JavaScript is single thread environment.
	 */
	private static LoggerWrap logUtil;
		static void setLogUtil( LoggerWrap logUtil) {
			CssStyleChangerBySelector.logUtil = logUtil;
			setJsniLogUtil( null);
		}
		static LoggerWrap getLogUtil() {
			return CssStyleChangerBySelector.logUtil;
		}
		
	/* Synchronization over jsniLogUtil member field is not necessary since 
	 * JavaScript is single thread environment.
	 */
	private static JsniLoggerWrap jsniLogUtil;
		protected static JsniLoggerWrap getJsniLogUtil() {
			if ( jsniLogUtil == null) {
				setJsniLogUtil( 
						JsniLoggerWrapFactoryImpl.getJsniLoggerWrap(  
								null, getLogUtil(), getNameForLogging(), getJsniLogUtilFactoryInterface())
						);
			}
			return jsniLogUtil;
		}
		protected static void setJsniLogUtil( JsniLoggerWrap jsniLogUtil) {
			CssStyleChangerBySelector.jsniLogUtil = jsniLogUtil;
		}
	// --------------------------------------------------------------------------------------------
	
//TODO I need to add logic to do sanity check on constructed style after calling decodeURIComponent
	public static final int Default_Minimum_Period = 50; 
	// When period is not bigger than or equal to 0, 
	// then initValue will be ignored, and endValue setting will take effect immediately.
	private static int minimumPeriod = Default_Minimum_Period;
		public static int getMinimumPeriod() {
			return minimumPeriod;
		}
		protected static void setMinimumPeriod( int minimumPeriod) {
			if ( minimumPeriod < 1) {
				throw new IllegalArgumentException( 
						"Value given by minimumPeriod argument is %1$d. It should be appropriate value " 
						+ "as whole duration, and at least it must be positive value."
						.replace( "%1$d", String.valueOf( minimumInterval))
						);
			}
			else if ( getMinimumInterval() >= minimumPeriod) {
				LoggerWrap logUtil = CssStyleChangerBySelector.getJsniLogUtil().getLoggerWrap();
				logUtil.log( 
						Level.WARNING, 
						"Setting the minimum period value (%1$d[ms]) shorter than the interval " 
						+ "value (%2$d[ms])."
						.replace( "%1$d", String.valueOf( minimumPeriod))
						.replace( "%2$d", String.valueOf( getMinimumInterval()))
						);
			}
			CssStyleChangerBySelector.minimumPeriod = minimumPeriod;
		}
	public static final int Default_Minimum_Interval = 50;
	private static int minimumInterval = Default_Minimum_Interval;
		public static int getMinimumInterval() {
			return minimumInterval;
		}
		protected static void setMinimumInterval( int minimumInterval) {
			if ( minimumInterval < 1) {
				throw new IllegalArgumentException( 
						"Value given by minimumInterval argument is %1$d. It should be appropriate value " 
						+ "as interval length, and at least it must be positive value."
						.replace( "%1$d", String.valueOf( minimumInterval))
						);
			}
			CssStyleChangerBySelector.minimumInterval = minimumInterval;
		}
		
	// For getting cscbsDataSet JavaScript variable -----------------------------------------------
	/**
	 * From hosted web page, read {@link JavaScriptObject} named after value obtained by  
	 * {@link JsDataPropertyIdentification#CscbsDataSet#getJsPropertyName()} method. 
	 * 
	 * @return <code>JavaScriptObject</code> named after value obtained by 
	 * {@link JsDataPropertyIdentification#CscbsDataSet#getJsPropertyName()} method or null when 
	 * it cannot find. 
	 * @throws JavaScriptException
	 */
	native JavaScriptObject getCssStyleChangerDataSetJsObj() throws JavaScriptException /*-{
		// Preparations for logging ---------------------------------------------------------------
		var methodName = "getCssStyleChangerDataSetJsObj JSNI";
		var thisJsniLogUtil 
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::getJsniLogUtil()();
		var jsniLogEntering
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogResult
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		
		jsniLogEntering( methodName, null);
		
		var cscbsDataSetId
		= @com.newmainsoftech.gwetons.client.style.css.JsDataPropertyIdentification::CscbsDataSet;
		var cscbsDataSetName 
		= cscbsDataSetId.@com.newmainsoftech.gwetons.client.style.css.JsDataPropertyIdentification::getJsPropertyName()();
		
		var cscbsDataSetType = typeof( $wnd[ cscbsDataSetName]);
			if ( cscbsDataSetType != "object") {
				jsniLogging( 
					@java.util.logging.Level::SEVERE, 
					"Type of " + cscbsDataSetName + " is not object: " + cscbsDataSetType);
				jsniLogResult( methodName, null);
				return null;
			}
		var cscbsDataSet = $wnd[ cscbsDataSetName];
		
		jsniLogResult( methodName, String( cscbsDataSet));
		return cscbsDataSet;
	}-*/;
	
	static CssStyleChangerDataSet cssStyleChangerDataSet = null;
		/**
		 * Nullify CssStyleChangerBySelector.cssStyleChangerDataSet member field.
		 */
		void initializeCssStyleChangerDataSet() {
			String methodName = "initializeCssStyleChangerDataSet";
			LoggerWrap logUtil = CssStyleChangerBySelector.getJsniLogUtil().getLoggerWrap();
			logUtil.entering( methodName, null);
			
//TODO need to stop and reinitialize timer
			CssStyleChangerBySelector.cssStyleChangerDataSet = null;
			
			logUtil.exiting( methodName);
		}
		
		CssStyleChangerDataSet getCssStyleChangerDataSet() {
			String methodName = "getCssStyleChangerDataSet";
			LoggerWrap logUtil = CssStyleChangerBySelector.getJsniLogUtil().getLoggerWrap(); 
			logUtil.entering( methodName, null);
			
			logUtil.exiting( 
					methodName, CssStyleChangerBySelector.cssStyleChangerDataSet);
			return CssStyleChangerBySelector.cssStyleChangerDataSet;
		}
		
		/**
		 * This method (re)loads cscbsDataSet JavaScript object to cssStyleChangerDataSet member field. 
		 * Explicitly call this for the case that cscbsDataSet JavaScript object is not available
		 * when constructor of this class is called.
		 */
		void loadCssStyleChangerDataSet() {
			String methodName = "loadCssStyleChangerDataSet";
			LoggerWrap logUtil = CssStyleChangerBySelector.getJsniLogUtil().getLoggerWrap(); 
			logUtil.entering( methodName, null);
			
			JavaScriptObject cssStyleChangerDataSetJsObj = getCssStyleChangerDataSetJsObj();
//TODO need to stop and reinitialize timer
				if ( cssStyleChangerDataSetJsObj == null) {
					CssStyleChangerBySelector.cssStyleChangerDataSet = null;
					logUtil.log( 
							Level.INFO, 
							"Nullified CssStyleChangerBySelector.cssStyleChangerDataSet member field"
							);
					logUtil.exiting( methodName);
					return;
				}
				
			CssStyleChangerDataSet instance = new CssStyleChangerDataSet( cssStyleChangerDataSetJsObj);
			CssStyleChangerBySelector.cssStyleChangerDataSet = instance;
				logUtil.log( 
						Level.INFO, 
						"Value of cssStyleChangerDataSet member field was overwritten"
						);
			
			logUtil.exiting( methodName);
			return;
		}
		
//TODO Need to create JSNI native method to initialize cssStyleChangerDataSet to null
		// Reason of that necessity is to prevent initializing cssStyleChangerDataSet by calling 
		// setCssStyleChangerDataSet method while processing.
		// In order to do that, I also need to create wrapper method of that JSNI native method being created.
		// So that, in that wrapper method, I can stop timer and call JSNI native method being created.
	// --------------------------------------------------------------------------------------------
	
	public CssStyleChangerBySelector() {
		loadCssStyleChangerDataSet();
	} // public CssStyleChangerBySelector()
	
/*	List<JsCssRule> styleSelectorArray = new LinkedList<JsCssRule>();
		public void initializeStyleSelectorArray() {
			styleSelectorArray = new LinkedList<JsCssRule>();
		}
		boolean isInStyleSelectorArray( JavaScriptObject cssRuleObj) {
			String methodName = "isInStyleSelectorArray";
			LogUtil logUtil 
			= LogUtilFactory.getLogUtil( 
					CssStyleChangerBySelector.getLogUtil(), 
					CssStyleChangerBySelector.getNameForLogging(), 
					CssStyleChangerBySelector.getLogUtilFactoryInterface()
					);
			logUtil.entering( methodName);
			
			for( JsCssRule jsCssRule : styleSelectorArray) {
				if ( jsCssRule.equals( cssRuleObj)) {
					logUtil.exiting( methodName, true);
					return true;
				}
			} // for
			
			logUtil.exiting( methodName, false);
			return false;
		}
*/
/*		
		boolean addCssRuleToStyleSelectorArray( JavaScriptObject cssRuleObj) {
			String methodName = "addCssRuleToStyleSelectorArray";
			LogUtil logUtil 
			= LogUtilFactory.getLogUtil( 
					CssStyleChangerBySelector.getLogUtil(), 
					CssStyleChangerBySelector.getNameForLogging(), 
					CssStyleChangerBySelector.getLogUtilFactoryInterface()
					);
			logUtil.entering( 
					methodName, "with argument of cssRuleObj (= " + cssRuleObj.toString() + ")"
					);
			
			if ( isInStyleSelectorArray( cssRuleObj)) {
				logUtil.exiting( methodName, false);
				return false;
			}
			
			JsCssRule jsCssRule = new JsCssRule( cssRuleObj);
			boolean additionResult = styleSelectorArray.add( jsCssRule);
			
			logUtil.exiting( methodName, additionResult);
			return additionResult;
		}
*/	
	// For interactive visual media types, 
	// refer to table at the bottom of http://www.w3.org/TR/CSS2/media.html -----------------------
	/**
	 * Default list of interactive CSS media types what is backed up by String array; thereby, attempt 
	 * of modification to it will yield UnsupportedOperationException.
	 */
	public final static List<String> DefaultSupportedStylesheetMediaTypeList
	= Arrays.asList( new String[]{ "all", "handheld", "projection", "screen", "tty", "tv"});
	/**
	 * List of interactive CSS media types. <br />
	 * Initialized to CssStyleChangerBySelector.DefaultSupportedStylesheetMediaTypeList.
	 */
	private static List<String> supportedStylesheetMediaTypeList
	= CssStyleChangerBySelector.DefaultSupportedStylesheetMediaTypeList;
		public static List<String> getSupportedStylesheetMediaTypeList() {
			return CssStyleChangerBySelector.supportedStylesheetMediaTypeList;
		}
		public static void setSupportedStylesheetMediaTypeList(
				List<String> supportedStylesheetMediaTypeList) {
			CssStyleChangerBySelector.supportedStylesheetMediaTypeList = supportedStylesheetMediaTypeList;
		}
	
//	static final native JsArrayString initSupportedStylesheetMediaTypeArray() /*-{
		//support only interactive visual media types: all, handheld, projection, screen, tty, tv
//		return [ "all", "handheld", "projection", "screen", "tty", "tv"];
//	}-*/;
//	public static final JsArrayString SupportedStylesheetMediaTypeArray 
//	= initSupportedStylesheetMediaTypeArray();

	// --------------------------------------------------------------------------------------------
	
	public static native boolean sanityCheckStyleSheetsObj() throws JavaScriptException /*-{
		// Preparations for logging ---------------------------------------------------------------
		var methodName = "sanityCheckStyleSheetsObj JSNI";
		var thisJsniLogUtil 
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::getJsniLogUtil()();
		var jsniLogEntering
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogResult
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		jsniLogEntering( methodName, null);
		
		// Sanity check on $doc.styleSheets -------------------------------------------------------
		var styleSheetsType = typeof( $doc.styleSheets);
			if ( styleSheetsType != "object") {
				jsniLogging( 
					@java.util.logging.Level::WARNING, 
					"Type of $doc.styleSheets is not object: " + styleSheetsType);
				jsniLogResult( methodName, false);
				return false;
			}
		if ( $doc.styleSheets == null) {
			jsniLogging( 
				@java.util.logging.Level::WARNING, 
				"$doc.styleSheets is null");
			jsniLogResult( methodName, false);
			return false;
		}
		if ( $doc.styleSheets.constructor === Object) {
			jsniLogging( 
				@java.util.logging.Level::WARNING, 
				"$doc.styleSheets.constructor does not refer to Object's one: " 
				+ String( $doc.styleSheets.constructor)
				);
			jsniLogResult( methodName, false);
			return false;
		}
		var propertyType = typeof( $doc.styleSheets.length);
			if ( propertyType != "number") {
				jsniLogging( 
					@java.util.logging.Level::WARNING, 
					"Type of $doc.styleSheets.length property is not number: " + propertyType
					);
				jsniLogResult( methodName, false);
				return false;
			}
		// ----------------------------------------------------------------------------------------
		
		jsniLogResult( methodName, true);
		return true;
	}-*/;

	static native String getStyleSheetPointer( int styleSheetIndex) 
	throws JavaScriptException /*-{
		// Preparation for logging ----------------------------------------------------------------
		var methodName = "getStyleSheetPointer JSNI";
		var thisJsniLogUtil 
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::getJsniLogUtil()();
		var jsniLogEntering
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogResult
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		
		jsniLogEntering( methodName, [ "styleSheetIndex(=" + String( styleSheetIndex) + ")"]);
		
		// Sanity check on $doc.styleSheets -------------------------------------------------------
		var sanityCheckStyleSheetsObjResult 
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::sanityCheckStyleSheetsObj()(
			);
		if ( !sanityCheckStyleSheetsObjResult) {
			jsniLogResult( methodName, null);
			return null;
		}
		// ----------------------------------------------------------------------------------------
		
		var styleSheetObjType = typeof( $doc.styleSheets[ styleSheetIndex]);
		if ( styleSheetObjType != "object") {
			jsniLogging( 
				@java.util.logging.Level::WARNING,
				"Type of $doc.styleSheets[ " + String( styleSheetIndex) + "] is not object: " 
				+ styleSheetObjType
				);
			jsniLogResult( methodName, null);
			return null;
		}
		
		var styleSheetObj = $doc.styleSheets[ styleSheetIndex];
		var styleSheetPointerStr;
	 	var propertyType = typeof( styleSheetObj.href);
		if ( ( propertyType == "string") && (styleSheetObj.href.length > 0)) {
			styleSheetPointerStr = styleSheetObj.href;
		}
		else {
			styleSheetPointerStr = "$doc.styleSheets[" + styleSheetIndex + "]";
		}
		
		jsniLogResult( methodName, styleSheetPointerStr);
		return styleSheetPointerStr;
	}-*/;
	
	/**
	 * @param styleSheetObj : an element of styleSheets
	 * @param stylesheetMediaTypeIterator : points collection of media types
	 * @param isDeafultMediaTypeAccepted : If this is true, then always return true when 
	 * media property value of styleSheetObj input is blank (what is default media type.) 
	 * If this is false, then always return false when media property value of styleSheetObj 
	 * input is blank. 
	 * @param isAllMatchRequired : If this is true, then return true only when media property value of 
	 * styleSheetObj input has all media types specified in collection being referred via 
	 * stylesheetMediaTypeIterator input. If this is false, then return true when media property value of 
	 * styleSheetObj input has one of media types specified in collection being referred via 
	 * stylesheetMediaTypeIterator input.
	 * @param logUtil : either LogUtil instance or null.
	 * @return true when media property of styleSheetObj input is found in media type collection 
	 * being referred via stylesheetMediaTypeIterator input. Otherwise false.
	 * @throws JavaScriptException
	 */
	static native boolean isStyleSheetMediaTypeSupported( 
			JavaScriptObject styleSheetObj, 
			Iterator stylesheetMediaTypeIterator,
			boolean isDeafultMediaTypeAccepted,
			boolean isAllMatchRequired) 
	throws JavaScriptException /*-{
		// Preparations for logging ---------------------------------------------------------------
		var methodName = "isStyleSheetMediaTypeSupported JSNI";
		var thisJsniLogUtil 
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::getJsniLogUtil()();
		var jsniLogEntering
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogResult
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		
		jsniLogEntering( 
			methodName, 
			[ "styleSheetObj(=" + String( styleSheetObj) + ")",  
				"stylesheetMediaTypeIterator(=" + String( stylesheetMediaTypeIterator) + ")", 
				"isDeafultMediaTypeAccepted(=" + String( isDeafultMediaTypeAccepted) + ")", 
				"isAllMatchRequired(=" + String( isAllMatchRequired) + ")"
			]);
			
		// Inputs validation ----------------------------------------------------------------------
		if ( ( typeof( styleSheetObj) != "object") || ( styleSheetObj == null)) {
			jsniLogging( 
				@java.util.logging.Level::WARNING, 
				"Invalid value as styleSheetObj input: " + String( styleSheetObj)
				);
			jsniLogResult( methodName, false);
			return false;
		}
		if ( ( typeof( stylesheetMediaTypeIterator) != "object") || ( stylesheetMediaTypeIterator == null)) {
			jsniLogging( 
				@java.util.logging.Level::WARNING, 
				"Invalid value as stylesheetMediaTypeIterator input: " + String( stylesheetMediaTypeIterator)
				);
			jsniLogResult( methodName, false);
			return false;
		}
		// ----------------------------------------------------------------------------------------
		
		var styleSheetPointerStr = String( styleSheetObj);
		if ( typeof( styleSheetObj.href) == "string") {
			if ( styleSheetObj.href.length > 0) {
			styleSheetPointerStr = styleSheetObj.href;
			}
		}
		
		// Saminty check on styleSheetObj.media property ------------------------------------------
		var mediaPropertyType = typeof( styleSheetObj.media);
		if ( styleSheetObj.media) {
			if ( ( mediaPropertyType != "string") && ( mediaPropertyType != "object")) {
				jsniLogging( 
					@java.util.logging.Level::WARNING, 
					"Unexpected type of media property of styleSheetObj input (=" + styleSheetPointerStr 
					+ ") as style sheet: " + mediaPropertyType
					);
				jsniLogResult( methodName, false);
				return false;
			}
			if ( mediaPropertyType == "object") {
				if ( typeof( styleSheetObj.media.mediaText) != "string") {
					jsniLogging( 
						@java.util.logging.Level::WARNING, 
						"Unexpected type of mediaText property of media property of styleSheetObj input "
						+ "(=" + styleSheetPointerStr + ") as style sheet: " 
						+ typeof( styleSheetObj.media.mediaText)
						);
					jsniLogResult( methodName, false);
					return false;
				}
			}
		}
		else {
			jsniLogging( 
				@java.util.logging.Level::INFO, 
				"media property of styleSheetObj input (=" + styleSheetPointerStr + ") is " 
				+ ((mediaPropertyType == "undefined") ? "undefined" : "null") + ". "
				+ "Considering it as default media."
				);
					// http://www.webreference.com/programming/javascript/rg21/index.html says that style sheet with no media is applied to all media types
					// But accroding to http://www.javascriptkit.com/dhtmltutors/externalcss2.shtml, default media type is screen
				
			if ( isDeafultMediaTypeAccepted) {
				jsniLogResult( methodName, true);
				return true;
			}
			else {
				jsniLogResult( methodName, false);
				return false;
			}
		}
		// ----------------------------------------------------------------------------------------
		
		var stylesheetMediaOntoBeSupported = false;
		// Regarding default media type -----------------------------------------------------------
		if ( mediaPropertyType == "string") { // IE
			if ( styleSheetObj.media.length < 1) {
			// default media type
				jsniLogging( 
					@java.util.logging.Level::FINE, 
					"media propety of styleSheetObj(=" + String( styleSheetObj) 
					+ ") input is empty string."
					);
					
				if ( isDeafultMediaTypeAccepted) {
					stylesheetMediaOntoBeSupported = true;
					jsniLogResult( methodName, stylesheetMediaOntoBeSupported);
					return stylesheetMediaOntoBeSupported;
				}
				else {
					stylesheetMediaOntoBeSupported = false;
					jsniLogResult( methodName, stylesheetMediaOntoBeSupported);
					return stylesheetMediaOntoBeSupported;
				}
			}
		}
		else { // Standard browser; mediaPropertyType == "object"
			if ( styleSheetObj.media.mediaText.length < 1) {
			// default media type
				jsniLogging( 
					@java.util.logging.Level::FINE, 
					"media.mediaText propety of styleSheetObj(=" + String( styleSheetObj) 
					+ ") input is empty string."
					);
					
				if ( isDeafultMediaTypeAccepted) {
					stylesheetMediaOntoBeSupported = true;
					jsniLogResult( methodName, stylesheetMediaOntoBeSupported);
					return stylesheetMediaOntoBeSupported;
				}
				else {
					stylesheetMediaOntoBeSupported = false;
					jsniLogResult( methodName, stylesheetMediaOntoBeSupported);
					return stylesheetMediaOntoBeSupported;
				}
			}
		}
		// ----------------------------------------------------------------------------------------
		
		// Regarding other than default media type ------------------------------------------------
		while( stylesheetMediaTypeIterator.@java.util.Iterator::hasNext()) {
			var supportedMediaType
			= stylesheetMediaTypeIterator.@java.util.Iterator::next();
			
			if ( mediaPropertyType == "string") { // IE
				if ( styleSheetObj.media.indexOf( String( supportedMediaType)) > -1) {
					stylesheetMediaOntoBeSupported = true;
					if ( !isAllMatchRequired) {
						break; // while
					}
				}
				else {
					stylesheetMediaOntoBeSupported = false;
					if ( isAllMatchRequired) {
						break; // while
					}
				}
			}
			else { // Standard browser; mediaPropertyType == "object"
				if ( styleSheetObj.media.mediaText.indexOf( String( supportedMediaType)) > -1) {
					stylesheetMediaOntoBeSupported = true;
					if ( !isAllMatchRequired) {
						break; // while
					}
				}
				else {
					stylesheetMediaOntoBeSupported = false;
					if ( isAllMatchRequired) {
						break; // while
					}
				}
			}
		} // while
		// ----------------------------------------------------------------------------------------
		
		jsniLogResult( methodName, stylesheetMediaOntoBeSupported);
		return stylesheetMediaOntoBeSupported;
	}-*/;
	
	/**
	 * Check whether stylesheet specified by styleSheetIndex input has default media type or media type 
	 * among one obtained by getSupportedStylesheetMediaTypeList method.
	 * @param styleSheetIndex
	 * @return
	 * @throws JavaScriptException
	 */
	static native boolean isStyleSheetMediaTypeSupported( int styleSheetIndex) throws JavaScriptException /*-{
		// Preparations for logging ---------------------------------------------------------------
		var methodName = "isStyleSheetMediaTypeSupported JSNI";
		var thisJsniLogUtil 
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::getJsniLogUtil()();
		var jsniLogEntering
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogResult
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		
		jsniLogEntering( methodName, [ "styleSheetIndex(=" + String( styleSheetIndex) + ")"]);
			
		// Sanity check on $doc.styleSheets -------------------------------------------------------
		var sanityCheckStyleSheetsObjResult 
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::sanityCheckStyleSheetsObj()(
			);
		if ( !sanityCheckStyleSheetsObjResult) {
			jsniLogResult( methodName, false);
			return false;
		}
		// ----------------------------------------------------------------------------------------
		
		// Sanity check on style sheet element ----------------------------------------------------
		var styleSheetObjType = typeof( $doc.styleSheets[ styleSheetIndex]);
		if ( styleSheetObjType != "object") {
			jsniLogging( 
				@java.util.logging.Level::WARNING, 
				"Type of document.styleSheets[ " + String( styleSheetIndex) + "] is not object: " 
				+ styleSheetObjType
				);
			jsniLogResult( methodName, false);
			return false;
		}
		
		var styleSheetObj = $doc.styleSheets[ styleSheetIndex];
			if ( styleSheetObj == null) {
				jsniLogging( 
					@java.util.logging.Level::WARNING, 
					"document.styleSheets[" + String(styleSheetIndex) + "]  is null."
					);
				jsniLogResult( methodName, false);
				return false;
			}
		
		var styleSheetPointerStr
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::getStyleSheetPointer(I)( 
			styleSheetIndex);
		// ----------------------------------------------------------------------------------------
		
		// Saminty check on styleSheetObj.media property ------------------------------------------
		var mediaPropertyType = typeof( styleSheetObj.media);
		if ( styleSheetObj.media) {
			if ( ( mediaPropertyType != "string") && ( mediaPropertyType != "object")) {
				jsniLogging( 
					@java.util.logging.Level::WARNING, 
					"Unexpected type of media property of styleSheetObj input (=" + styleSheetPointerStr 
					+ ") as style sheet: " + mediaPropertyType
					);
				jsniLogResult( methodName, false);
				return false;
			}
			if ( mediaPropertyType == "object") {
				if ( typeof( styleSheetObj.media.mediaText) != "string") {
					jsniLogging( 
						@java.util.logging.Level::WARNING, 
						"Unexpected type of mediaText property of media property of styleSheetObj input "
						+ "(=" + styleSheetPointerStr + ") as style sheet: " 
						+ typeof( styleSheetObj.media.mediaText)
						);
					jsniLogResult( methodName, false);
					return false;
				}
			}
		}
		else {
			jsniLogging( 
				@java.util.logging.Level::INFO, 
				"media property of styleSheetObj input (=" + styleSheetPointerStr + ") is " 
				+ ((mediaPropertyType == "undefined") ? "undefined" : "null") + ". "
				+ "Considering it as default media (either all or screen) what is supported media type."
				);
					// http://www.webreference.com/programming/javascript/rg21/index.html says that style sheet with no media is applied to all media types
					// But accroding to http://www.javascriptkit.com/dhtmltutors/externalcss2.shtml, default media type is screen
				
			jsniLogResult( methodName, true);
			return true;
		}
		// ----------------------------------------------------------------------------------------
		
		//supporting only interactive visual media types: handheld, projection, screen, tty, tv
		var supportedStylesheetMediaTypeList 
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::getSupportedStylesheetMediaTypeList()(
			);
		var supportedStylesheetMediaTypeIterator
		= supportedStylesheetMediaTypeList.@java.util.List::iterator()();
		var isStyleSheetMediaTypeSupported
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::isStyleSheetMediaTypeSupported(Lcom/google/gwt/core/client/JavaScriptObject;Ljava/util/Iterator;ZZ)(
			styleSheetObj, supportedStylesheetMediaTypeIterator, true, false);
		
		jsniLogResult( methodName, isStyleSheetMediaTypeSupported);
		return isStyleSheetMediaTypeSupported;
	}-*/;
	
	public static native JsArray<JavaScriptObject> getCssRules( 
			String styleSelectorStr, 
			Iterator<String> stylesheetMediaTypeIterator,
			boolean isDeafultMediaTypeAccepted,
			boolean isAllTypesMatchRequired, 
			boolean includeDesabledStyleSheet) 
	throws JavaScriptException /*-{
		// Preparations for logging ---------------------------------------------------------------
		var methodName = "getCssRules JSNI";
		var thisJsniLogUtil 
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::getJsniLogUtil()();
		var jsniLogEntering
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogResult
		= thisJsniLogUtil.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		
		jsniLogEntering(  
			methodName, 
			[ "styleSelectorStr(=" + String( styleSelectorStr) + ")", 
				"stylesheetMediaTypeIterator(=" + String( stylesheetMediaTypeIterator) + ")", 
				"isDeafultMediaTypeAccepted(=" + String( isDeafultMediaTypeAccepted) + ")", 
				"isAllTypesMatchRequired(=" + String( isAllTypesMatchRequired) + ")", 
				"includeDesabledStyleSheet(=" + String( includeDesabledStyleSheet) + ")"
			]);
			
		// Sanity check on $doc.styleSheets -------------------------------------------------------
		var sanityCheckStyleSheetsObjResult 
		= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::sanityCheckStyleSheetsObj()(
			);
		if ( !sanityCheckStyleSheetsObjResult) {
			jsniLogResult( methodName, null);
			return null;
		}
		// ----------------------------------------------------------------------------------------
		
		// Trim styleSelectorStr input
		var styleSelectorStrCopy = styleSelectorStr.replace( new RegExp( "^\\s*"), "");
		styleSelectorStrCopy = styleSelectorStrCopy.replace( new RegExp( "\\s*$"), "");
		
		var cssRuleArray = new Array();
		
		// Find style specified by styleSelectorStr selector from style sheets, 
		// and add it to cssRuleArray -------------------------------------------------------------
		for( 
				var styleSheetPropertyIndex=0; 
				styleSheetPropertyIndex < $doc.styleSheets["length"]; 
				styleSheetPropertyIndex++
				) 
		{
			var styleSheetObj = $doc.styleSheets[ styleSheetPropertyIndex];
			
			var styleSheetPointerStr
			= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::getStyleSheetPointer(I)( 
				styleSheetPropertyIndex);
				
			if ( styleSheetObj.disabled) {
				if ( !includeDesabledStyleSheet) {
					jsniLogging( 
						@java.util.logging.Level::FINEST, 
						"Skipping " + styleSheetPointerStr + " stylesheet becaust it is in desabled state."
						);
					continue; // for
				} 
				else {
					jsniLogging( 
						@java.util.logging.Level::FINEST, 
						"Although " + styleSheetPointerStr + " stylesheet is in desabled state, "
						+ "it is used to search CSS rule for " + styleSelectorStr + " selector."
						);
				}
			}
			
			// Check whether media of styleSheetObj is supported one --------------------------
			var isStyleSheetMediaTypeSupported 
			= @com.newmainsoftech.gwetons.client.style.css.CssStyleChangerBySelector::isStyleSheetMediaTypeSupported(Lcom/google/gwt/core/client/JavaScriptObject;Ljava/util/Iterator;ZZ)(
				styleSheetObj, 
				stylesheetMediaTypeIterator, 
				isDeafultMediaTypeAccepted, 
				isAllTypesMatchRequired
				);
				
			// Skip styleSheetObj when its media is not supported one
			if ( !isStyleSheetMediaTypeSupported) continue; // for
			
			jsniLogging( 
				@java.util.logging.Level::FINEST, 
				"Located style sheet (with supported media type(s)): " + styleSheetPointerStr
				);
			// --------------------------------------------------------------------------------
			
			try {
				if ( styleSheetObj.rules) { 
				// for IE: this needs to come ahead since IE also has different cssRules property under styleSheet
					for( var cssRuleIndex in styleSheetObj.rules) {
						if ( isNaN( cssRuleIndex)) continue;
						
						var cssRuleObj = styleSheetObj.rules[ cssRuleIndex];
						if ( typeof( cssRuleObj.selectorText) == "string") {
							if ( cssRuleObj.selectorText.toUpperCase() == styleSelectorStrCopy.toUpperCase()) {
							// IE capitalizes the selectorText portion
								
								cssRuleArray.push( cssRuleObj);
									jsniLogging(
										@java.util.logging.Level::FINEST, 
										"Detected that rules[" + cssRuleIndex + "] of " 
										+ styleSheetPointerStr + " contains selectorText property "
										+ "with specified value (=" + styleSelectorStrCopy + ")"
										);
							}
						}
					} // for
				}
				else if ( styleSheetObj.cssRules) {
				// Standard browser
					for( var cssRuleIndex in styleSheetObj.cssRules) {
						if ( isNaN( cssRuleIndex)) continue;
						
						var cssRuleObj = styleSheetObj.cssRules[ cssRuleIndex];
						if ( typeof( cssRuleObj.selectorText) == "string") {
							if ( cssRuleObj.selectorText.toUpperCase() == styleSelectorStrCopy.toUpperCase()) {
							// IE capitalizes the selectorText portion
							
								cssRuleArray.push( cssRuleObj);
									jsniLogging(
										@java.util.logging.Level::FINEST, 
										"Detected that cssRules[" + cssRuleIndex + "] of " 
										+ styleSheetPointerStr + " contains selectorText property "
										+ "with specified value (=" + styleSelectorStrCopy + ")"
										);
							}
						}
					} // for
				}
			}
			catch( exception) { 
			// Catch access deinded error by accessing stylesheet loaded from other domain
				var errorMessage 
				= "Skipped processing the stylesheet " + styleSheetPointerStr 
				+ " due to this error: " + String( exception); 
				jsniLogging( 
					@java.util.logging.Level::INFO, 
					errorMessage);
				continue; // for
			}
		} // for
		// ----------------------------------------------------------------------------------------
		
		if ( cssRuleArray.length < 1) {
			var message 
			= "Could not find any styleSheet element with meida property value subjected by " 
			+ "stylesheetMediaTypeIterator(=" + String( stylesheetMediaTypeIterator) + "), "
			+ "isDeafultMediaTypeAccepted(=" + String( isDeafultMediaTypeAccepted) + "), "
			+ "isAllTypesMatchRequired(=" + String( isAllTypesMatchRequired) + "), and "
			+ "includeDesabledStyleSheet(=" + String(includeDesabledStyleSheet) + ") inputs, "
			+ "and containing the CSS rule of what selectorText property value is identical to "
			+ "specifed value: " + styleSelectorStrCopy;
			jsniLogging( @java.util.logging.Level::FINE, message);
			
			jsniLogResult( methodName, "[]");
		}
		else {
			jsniLogResult( methodName, String( cssRuleArray));
		}
		
		return cssRuleArray;
	}-*/;
	
	// Cautions of using Stylesheet cssRules collection
	// Check for detail at:
	// 		http://www.quirksmode.org/dom/w3c_css.html
	// 		http://www.howtocreate.co.uk/tutorials/javascript/domstylesheets
	// Mozilla does not provide this collection for alternate stylesheets that do not have a title attribute.
	// This only includes rules that are in the root of the stylesheet, and not, for example in 
	// a @media block.
	// Browsers will not include any rules that they do not understand, such as rules using a selector 
	// that they do not recognize as being in a valid format. 
	// This means that the cssRules collection will almost certainly be a different length in different 
	// browsers, and you cannot rely on it being a certain size if you use features that are not supported 
	// by all the browsers.
	/**
	 * Change value of css style rule (which is identified by styleSelectorStr and styleStr)  
	 * in styleSelectorArray to valueStr. <br />
	 * Prerequisite: change on css style rule won't be reflected on browser unless css style rule 
	 * (which is subject to change) has been actually in style sheet before executing this method. 
	 * In other words, this method just update css style rule in styleSelectorJsArray, which is  
	 * not necessary to mean that css style rule is in any style sheet. <br />
	 * Note: This won't do any sanitation on styleStr and valueStr inputs.
	 */
/*	void changeStyle( String styleSelectorStr, String styleStr, String valueStr) {
//TODO sanitize styleStr and valueStr
		String methodName = "changeStyle";
		LogUtil logUtil 
		= LogUtilFactory.getLogUtil( 
				CssStyleChangerBySelector.getLogUtil(), 
				CssStyleChangerBySelector.getNameForLogging(), 
				CssStyleChangerBySelector.getLogUtilFactoryInterface()
				);
		logUtil.entering( 
				methodName, 
				"with argument of styleSelectorStr (= " + styleSelectorStr + "), " 
				+ "styleStr (= " + styleStr + ") and valueStr (=" + valueStr + ")" 
				);

		String styleSelectorStrCopy = styleSelectorStr.trim();
		for( JsCssRule jsCssRule : styleSelectorArray) {
			if ( jsCssRule.getSelectorText().equalsIgnoreCase( styleSelectorStrCopy)) {
				jsCssRule.setStyle( styleStr, valueStr);
			}
		} // for
		
		logUtil.exiting( methodName);
	}
*/
	List<StyleChangerTimerEventManager> styleChangerTimerEventManagerList 
	= new ArrayList<StyleChangerTimerEventManager>();
	
	/**
	 * This, first, set initial values of all css style rules at once, then process each 
	 * css style rule as specified to end value in sequence order.  
	 */
	public void startStyleChanges() {
		String methodName = "startStyleChange";
		LoggerWrap logUtil = CssStyleChangerBySelector.getJsniLogUtil().getLoggerWrap(); 
		logUtil.entering( methodName, null);
		
		if ( cssStyleChangerDataSet == null) {
			loadCssStyleChangerDataSet();
			
			if ( cssStyleChangerDataSet == null) {
				logUtil.exiting( methodName);
				return;
			}
		}
		
		for( CssStyleChangerDataUnit cssStyleChangerDataUnit 
				: cssStyleChangerDataSet.getStyleChangerDataList()) {
			StyleChangerTimerEventManager styleChangerTimerEventManager
			= new StyleChangerTimerEventManager( cssStyleChangerDataUnit);
			styleChangerTimerEventManagerList.add( styleChangerTimerEventManager);
			
			styleChangerTimerEventManager.beginStyleChangerTimerEvent();
		} // for
		
		logUtil.exiting( methodName);
	} // public void startStyleChange()
}
